﻿Imports System.Reflection
Imports SystemInfoReporter.Core

Module modMain

    'List of collector references.
    Dim collectors As List(Of ObjectRef(Of Collector.ICollector))
    'List of writer references.
    Dim writers As List(Of ObjectRef(Of Writer.IWriter))
    'List of writers to be passed to collector Startup() methods.
    Dim writersList As List(Of Writer.IWriter)
    'List of plugins.
    Dim plugins As List(Of Plugin)

    'Name of system to be audited.
    Dim systemName As String

    'Compression flag.
    Dim compressOutput As Boolean

    'Program entry point.
    Sub Main()
        Try
            'Read configuration - file name of configuration passed as parameter.
            ReadConfiguration(Command())

            'Instantiate collector and writer objects.
            InstantiatePlugins()

            'Initialize writer outputs.
            InitializerWriters()

            'Initialize collectors.
            InitializeCollectors()

            'Run all collectors.
            RunCollectors()

            'Shutdown all collectors.
            ShutdownCollectors()

            'Shutdown all writers.
            ShutdownWriters()

        Catch ex As Exception
            Console.WriteLine("An unexpected error has occurred: " + ex.ToString())
        End Try
    End Sub

    'Reads the configuration file.
    Private Sub ReadConfiguration(ByVal cfgFile As String)

        'Table with system name.
        Const REPORTERCONF_TABLE As String = "ReporterConfiguration"
        'Table with plugin records.
        Const PLUGIN_TABLE As String = "PluginRec"

        'If nothing was specified, signal an error.
        If cfgFile = "" Then
            Console.WriteLine("Usage: SystemInfoReporter <Configuration File>")
            End
        End If

        'Read the configuration in a dataset.
        Dim ds As New DataSet()
        ds.ReadXml(cfgFile)

        'Get system name.
        systemName = Convert.ToString(ds.Tables(REPORTERCONF_TABLE).Rows(0).Item(0))
        compressOutput = Convert.ToBoolean(ds.Tables(REPORTERCONF_TABLE).Rows(0).Item(1))

        plugins = New List(Of Plugin)

        'Read all plugins.
        For i As Integer = 0 To ds.Tables(PLUGIN_TABLE).Rows.Count - 1
            With ds.Tables(PLUGIN_TABLE).Rows(i)
                Dim plug As New Plugin(Convert.ToString(.Item("Assembly")), _
                                       Convert.ToString(.Item("Class")), _
                                       Convert.ToString(.Item("Configuration")), _
                                       Convert.ToString(.Item("Action")))
                plugins.Add(plug)
            End With
        Next

    End Sub

    'Loads assembly files and instantiates plugin objects.
    Private Sub InstantiatePlugins()

        collectors = New List(Of ObjectRef(Of Collector.ICollector))
        writers = New List(Of ObjectRef(Of Writer.IWriter))

        'Run through all plugins to determine the unique assemblies
        'that need to be loaded.
        Dim ht As New Hashtable()

        For i As Integer = 0 To plugins.Count - 1
            'Get the assembly file of the plugin. Using FileInfo helps
            'us get the full file name including the path even if only
            'the dll name was specified.
            Dim asmFile As String = New System.IO.FileInfo(plugins(i).AssemblyFile).FullName()
            'Do we have it in the hash table?
            If ht(asmFile) Is Nothing Then
                'No, load assembly and add it to the hash table.
                Dim asm As Assembly = Assembly.LoadFile(asmFile)
                plugins(i).AssemblyName = asm.FullName
                ht.Add(asmFile, asm.FullName)
            Else
                'We have it (and already loaded it).
                plugins(i).AssemblyName = CType(ht.Item(asmFile), String)
            End If
        Next

        'Run through all plugins to create the reference objects.
        For i As Integer = 0 To plugins.Count - 1
            If plugins(i).Type = Plugin.PluginType.Writer Then
                writers.Add(New ObjectRef(Of Writer.IWriter)(CType(Activator.CreateInstance(plugins(i).AssemblyName, plugins(i).ClassName).Unwrap(), Writer.IWriter), plugins(i)))
            Else
                collectors.Add(New ObjectRef(Of Collector.ICollector)(CType(Activator.CreateInstance(plugins(i).AssemblyName, plugins(i).ClassName).Unwrap(), Collector.ICollector), plugins(i)))
            End If
        Next

    End Sub

    'Initialize all writers and create the writers list.
    Public Sub InitializerWriters()

        writersList = New List(Of Writer.IWriter)
        For i As Integer = 0 To writers.Count - 1
            'Startup the writer.
            writers(i).Object().Startup(writers(i).Plugin().ConfigurationFile)
            'Add it to the list.
            writersList.Add(writers(i).Object())
            'Output the title, systen name and our version information.
            writers(i).Object().WriteTitle("System Info Report")
            writers(i).Object().WriteSystem(systemName + " (" + _
                                                Now.Year.ToString() + "/" + _
                                                Now.Month.ToString().PadLeft(2, "0"c) + "/" + _
                                                Now.Day.ToString().PadLeft(2, "0"c) + " " + _
                                                Now.Hour.ToString().PadLeft(2, "0"c) + ":" + _
                                                Now.Minute.ToString().PadLeft(2, "0"c) + ":" + _
                                                Now.Second.ToString().PadLeft(2, "0"c) + ")")
            writers(i).Object().WriteVersion(Assembly.GetExecutingAssembly().GetName().Version().ToString())
        Next

    End Sub

    'Initialize all collectors.
    Public Sub InitializeCollectors()

        For i As Integer = 0 To collectors.Count - 1
            collectors(i).Object().Startup(collectors(i).Plugin().ConfigurationFile, writersList)
        Next

    End Sub

    'Run all collectors.
    Public Sub RunCollectors()

        For i As Integer = 0 To collectors.Count - 1
            collectors(i).Object().Run()
        Next

    End Sub

    'Shutdown collectors.
    Public Sub ShutdownCollectors()

        For i As Integer = 0 To collectors.Count - 1
            collectors(i).Object().Cleanup()
        Next

    End Sub

    'Shutdown writers.
    Public Sub ShutdownWriters()

        Writer.WritersHelper.WriteFooter(writersList)
        Writer.WritersHelper.Cleanup(writersList)

        If Not compressOutput Then Exit Sub

        'Compress each file output, if there is one.
        For Each wrt As Writer.IWriter In writersList
            Dim fName As String = wrt.GetOutputFile
            If fName IsNot Nothing Then
                Try
                    Dim fZip As New ICSharpCode.SharpZipLib.Zip.FastZip
                    Dim FI As New IO.FileInfo(fName)
                    fZip.CreateZip(fName + ".zip", FI.DirectoryName, False, FI.Name, "")
                    fZip = Nothing
                Catch ex As Exception
                    'Intentionally do nothing here.
                Finally
                    IO.File.Delete(fName)
                End Try
            End If
        Next

    End Sub

End Module
